package com.ikingtech.framework.sdk.pay.embedded.supplier.allinpay;

import com.ikingtech.framework.sdk.context.exception.FrameworkException;
import com.ikingtech.framework.sdk.enums.pay.CashierSupplierEnum;
import com.ikingtech.framework.sdk.enums.pay.PayTypeEnum;
import com.ikingtech.framework.sdk.pay.embedded.Cashier;
import com.ikingtech.framework.sdk.pay.embedded.supplier.*;
import com.ikingtech.framework.sdk.utils.Tools;
import com.ikingtech.framework.sdk.pay.embedded.supplier.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;

/**
 * @author tie yan
 */
@Slf4j
@RequiredArgsConstructor
public class AllInPayCashier implements Cashier {

    private CashierSupplierConfig config;

    @Override
    public PayOrder pay(PayArgs payArgs) {
        String amountStr = Double.toString(payArgs.getAmount() * 100);
        String resultStr = Tools.Http.postForm(
                "https://vsp.allinpay.com/apiweb/unitorder/pay",
                AllInPaySybPayParam.builder()
                        .appid(this.config.getAppId())
                        .cusid(this.config.getCustomId())
                        .body(payArgs.getSubject())
                        .trxamt(amountStr.substring(0, amountStr.indexOf(".")))
                        .notify_url(this.config.getNotifyUrl() + "/" + payArgs.getRequestId())
                        .paytype(this.getSybPayType(payArgs.getPayType()).type)
                        .randomstr(payArgs.getRequestId())
                        .reqsn(payArgs.getRequestId())
                        .signtype("RSA")
                        .acct(payArgs.getChannelUserId())
                        .sub_appid(payArgs.getChannelAppId())
                        .build()
                        .toQueryStr(this.config.getApiKey())
        );
        AllInPaySybPayResult result = Tools.Json.toBean(resultStr, AllInPaySybPayResult.class);
        if (null == result) {
            throw new FrameworkException(Tools.Str.format("支付结果为空[{}]", resultStr));
        }
        if (result.fail()) {
            throw new FrameworkException(Tools.Str.format("支付失败[{}{}]", result.getRetmsg(), result.getErrmsg()));
        }
        PayOrder payOrder = new PayOrder();
        payOrder.setSupplierTradeId(result.getTrxid());
        payOrder.setChannelTradeId(result.getChnltrxid());
        payOrder.setRequestId(result.getReqsn());
        payOrder.setPayInfo(result.getPayinfo());
        return payOrder;
    }

    @Override
    public CancelResult cancel(CancelArgs args) {
        String amountStr = Double.toString(args.getAmount() * 100);
        String resultStr = Tools.Http.postForm(
                "https://vsp.allinpay.com/apiweb/tranx/cancel",
                AllInPaySybPayParam.builder()
                        .appid(this.config.getAppId())
                        .cusid(this.config.getCustomId())
                        .reqsn(Tools.Id.uuid())
                        .randomstr(Tools.Id.uuid())
                        .oldreqsn(args.getRequestId())
                        .trxamt(amountStr.substring(0, amountStr.indexOf(".")))
                        .signtype("RSA")
                        .build()
                        .toQueryStr(this.config.getApiKey())
        );
        AllInPaySybPayResult result = Tools.Json.toBean(resultStr, AllInPaySybPayResult.class);
        if (null == result) {
            return new CancelResult(false, Tools.Str.format("取消支付结果为空[{}]", resultStr));
        }
        if (result.fail()) {
            return new CancelResult(false, Tools.Str.format("取消支付失败[{}]", result.getRetmsg()));
        }
        return new CancelResult();
    }

    @Override
    public RefundResult refund(RefundArgs args) {
        String amountStr = Double.toString(args.getRefundAmount() * 100);
        String resultStr = Tools.Http.postForm(
                "https://vsp.allinpay.com/apiweb/tranx/refund",
                AllInPaySybPayParam.builder()
                        .appid(this.config.getAppId())
                        .cusid(this.config.getCustomId())
                        .reqsn(Tools.Id.uuid())
                        .randomstr(Tools.Id.uuid())
                        .oldreqsn(args.getRequestId())
                        .trxamt(amountStr.substring(0, amountStr.indexOf(".")))
                        .signtype("RSA")
                        .build().toQueryStr(this.config.getApiKey())
        );
        AllInPaySybPayResult result = Tools.Json.toBean(resultStr, AllInPaySybPayResult.class);
        if (null == result) {
            return new RefundResult(Tools.Str.format("[PAY][通联支付]退款结果为空[{}]", resultStr));
        }
        if (result.fail()) {
            return new RefundResult(Tools.Str.format("[PAY][通联支付]退款失败[{}]", result.getErrmsg()));
        }
        return new RefundResult();
    }

    @Override
    public RedPackResult redPack(RedPackArgs request) {
        throw new FrameworkException("暂不支持");
    }

    @Override
    public RedPackResult readPackConfirm(RedPackArgs args) {
        throw new FrameworkException("暂不支持");
    }

    @Override
    public PayResult validate(String resultParam, Map<String, Object> resultParamMap) {
        String sign = (String) resultParamMap.remove("sign");
        String validateSign = Tools.Encrypt.rsa(Tools.Http.toQueryStr(resultParamMap), this.config.getApiKey());
        if (Tools.Str.equals(sign, validateSign)) {
            throw new FrameworkException("[PAY][通联支付]交易结果验签失败");
        }
        PayResult result = new PayResult();
        result.setRequestId((String) resultParamMap.get("cusorderid"));
        result.setSupplierTradeId((String) resultParamMap.get("trxid"));
        result.setChannelTradeId((String) resultParamMap.get("chnltrxid"));
        result.setTradeTime(Tools.DateTime.Parser.pure((String) resultParamMap.get("paytime")));
        result.setSuccess("0000".equals(resultParamMap.get("trxstatus")));
        result.setCause((String) resultParamMap.get("trxstatus"));
        result.setSupplierResponse("success");
        return result;
    }

    @Override
    public PayResult getPayResult(GetPayResultArgs args) {
        String resultStr = Tools.Http.postForm(
                "https://vsp.allinpay.com/apiweb/tranx/query",
                AllInPaySybPayParam.builder()
                        .appid(this.config.getAppId())
                        .cusid(this.config.getCustomId())
                        .reqsn(args.getRequestId())
                        .randomstr(Tools.Id.uuid())
                        .trxid(args.getSupplierTradeId())
                        .signtype("RSA")
                        .build()
                        .toQueryStr(this.config.getApiKey())
        );
        AllInPaySybPayResult result = Tools.Json.toBean(resultStr, AllInPaySybPayResult.class);
        PayResult payResult = new PayResult();
        if (null == result) {
            payResult.setSuccess(false);
            payResult.setCause(Tools.Str.format("[PAY][通联支付]查询交易结果返回为空[{}]", resultStr));
            return payResult;
        }
        if (result.fail()) {
            payResult.setSuccess(false);
            payResult.setCause(Tools.Str.format("[PAY][通联支付]查询交易结果失败[{}]", resultStr));
            return payResult;
        }
        payResult.setRequestId(result.getReqsn());
        payResult.setSupplierTradeId(result.getTrxid());
        payResult.setChannelTradeId(result.getChnltrxid());
        if (null != result.getFintime()) {
            payResult.setTradeTime(Tools.DateTime.Parser.pure(result.getFintime()));
        }
        // 2008 或 2000 表示当前订单正在交易中
        payResult.setPaying("2008".equals(result.getTrxstatus()) || "2000".equals(result.getTrxstatus()));
        // 0000 表示交易成功，以外的表示交易中或交易失败
        payResult.setSuccess("0000".equals(result.getTrxstatus()));
        payResult.setCause(Tools.Str.format("[{}]{}", result.getTrxstatus(), result.getErrmsg()));
        payResult.setSupplierResponse("success");
        return payResult;
    }

    @Override
    public Cashier config(CashierSupplierConfig config) {
        this.config = config;
        return this;
    }

    @Override
    public CashierSupplierEnum supplier() {
        return CashierSupplierEnum.ALL_IN_PAY;
    }

    protected static final Map<PayTypeEnum, AllInSybPayTypeEnum> PAY_TYPE_MAP = Tools.Coll.newMap(
            Tools.Coll.Kv.of(PayTypeEnum.WECHAT_JS, AllInSybPayTypeEnum.WECHAT_JS),
            Tools.Coll.Kv.of(PayTypeEnum.WECHAT_SCAN, AllInSybPayTypeEnum.WECHAT_SCAN),
            Tools.Coll.Kv.of(PayTypeEnum.WECHAT_MINI, AllInSybPayTypeEnum.WECHAT_MINI)
    );

    protected AllInSybPayTypeEnum getSybPayType(PayTypeEnum payType) {
        return PAY_TYPE_MAP.get(payType);
    }
}
